AWS CloudShellにDuckDBをインストールしてS3にSQLを投げてみた
本記事では、AWSのマネジメントコンソールに統合されているリージョナルなターミナル環境「AWS CloudShell」を利用し、OLAPが得意なDBMSであるDuckDB経由でS3上のCSVファイルにSQLを投げる方法を紹介します。
ローカル環境にインストールしたり、EC2を立ち上げたりせず、大きすぎないCSV/Parquet/Icebergなファイルに探索的にSQLを実行したいようなケースで有用です。
1. CloudShellとS3の参照可能な権限でAWSコンソールにログイン
今回の動作確認では、AWS CloudShellからS3上のファイルを読み込みます。
CloudShellの操作とS3の参照権限のあるIAMユーザー・ロールで操作対象のAWSアカウントにログインしましょう。
2. CloudShell にアクセスしduckdbをインストール
AWS コンソールのメニューからCloudShellを起動します。
2024年12月時点のCPUアーキテクチャーはx86_64です。
$ uname -m
x86_64
- command line
- Linux
- x86_64
に対応するバイナリをダウンロードします。
$ VERSION=1.1.3
$ wget https://github.com/duckdb/duckdb/releases/download/v${VERSION}/duckdb_cli-linux-amd64.zip && unzip duckdb_cli-linux-amd64.zip
$ ls -1
duckdb
duckdb_cli-linux-amd64.zip
$ $ ./duckdb
v1.1.3 19864453f7
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
D select version();
┌─────────────┐
│ "version"() │
│ varchar │
├─────────────┤
│ v1.1.3 │
└─────────────┘
D
3. S3 にファイルを用意
Vanderbilt大学の生物統計学科でホストされている次のタイタニックのデータセットを取得し、S3にアップロードします。
$ head -n 3 titanic3.csv
"pclass","survived","name","sex","age","sibsp","parch","ticket","fare","cabin","embarked","boat","body","home.dest"
1,1,"Allen, Miss. Elisabeth Walton","female",29,0,0,"24160",211.3375,"B5","S","2",,"St Louis, MO"
1,1,"Allison, Master. Hudson Trevor","male",0.92,1,2,"113781",151.5500,"C22 C26","S","11",,"Montreal, PQ / Chesterville, ON"
S3にアップロードしたS3オブジェクトのS3 URIを取得します。
s3://YOUR-BUCKET-NAME/titanic3.csv
CloudShellからこのS3オブジェクトにアクセスできることを確認してください
$ aws s3 ls s3://YOUR-BUCKET-NAME/titanic3.csv
2024-12-29 12:52:50 116752 titanic3.csv
4. DuckDBからS3のCSVファイルにアクセス
DuckDB CLIのデフォルト設定では、S3にアクセスできません。
$ ./duckdb
D select * from read_csv('s3://YOUR-BUCKET/titanic3.csv');
HTTP Error: HTTP GET error on 'https://YOUR-BUCKET.s3.amazonaws.com/titanic3.csv' (HTTP 403)
httpfs エクステンションを有効にし、AWS SDKと同じ仕組みでクレデンシャルを取得する SECRET を定義します。
D INSTALL httpfs;
D LOAD httpfs;
D CREATE SECRET (
TYPE S3,
PROVIDER CREDENTIAL_CHAIN
);
┌─────────┐
│ Success │
│ boolean │
├─────────┤
│ true │
└─────────┘
永続的なキーを利用する場合は以下のようにSECRETを定義します
CREATE SECRET (
TYPE S3,
KEY_ID 'AKIAXXX',
SECRET 'wJalXXX',
REGION 'ap-northeast-1'
);
この状態でS3に問い合わせましょう
D select * from read_csv('s3://YOUR-BUCKET-NAME/titanic3.csv') limit 3;
┌────────┬──────────┬──────────────────────┬─────────┬───┬──────────┬─────────┬───────┬──────────────────────┐
│ pclass │ survived │ name │ sex │ … │ embarked │ boat │ body │ home.dest │
│ int64 │ int64 │ varchar │ varchar │ │ varchar │ varchar │ int64 │ varchar │
├────────┼──────────┼──────────────────────┼─────────┼───┼──────────┼─────────┼───────┼──────────────────────┤
│ 1 │ 1 │ Allen, Miss. Elisa… │ female │ … │ S │ 2 │ │ St Louis, MO │
│ 1 │ 1 │ Allison, Master. H… │ male │ … │ S │ 11 │ │ Montreal, PQ / Che… │
│ 1 │ 0 │ Allison, Miss. Hel… │ female │ … │ S │ │ │ Montreal, PQ / Che… │
├────────┴──────────┴──────────────────────┴─────────┴───┴──────────┴─────────┴───────┴──────────────────────┤
│ 3 rows 14 columns (8 shown) │
└────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
D select survived, count(*) from read_csv('s3://YOUR-BUCKET-NAME/titanic3.csv') group by survived order by survived;
┌──────────┬──────────────┐
│ survived │ count_star() │
│ int64 │ int64 │
├──────────┼──────────────┤
│ 0 │ 809 │
│ 1 │ 500 │
└──────────┴──────────────┘
同じデータに対して探索的に調べる場合、データを CTAS でメモリ上のテーブルに展開すると良いでしょう。
D CREATE TABLE titanic AS SELECT * FROM read_csv('s3://YOUR-BUCKET-NAME/titanic3.csv') ;
D SHOW titanic;
┌─────────────┬─────────────┬─────────┬─────────┬─────────┬─────────┐
│ column_name │ column_type │ null │ key │ default │ extra │
│ varchar │ varchar │ varchar │ varchar │ varchar │ varchar │
├─────────────┼─────────────┼─────────┼─────────┼─────────┼─────────┤
│ pclass │ BIGINT │ YES │ │ │ │
│ survived │ BIGINT │ YES │ │ │ │
│ name │ VARCHAR │ YES │ │ │ │
│ sex │ VARCHAR │ YES │ │ │ │
│ age │ DOUBLE │ YES │ │ │ │
│ sibsp │ BIGINT │ YES │ │ │ │
│ parch │ BIGINT │ YES │ │ │ │
│ ticket │ VARCHAR │ YES │ │ │ │
│ fare │ DOUBLE │ YES │ │ │ │
│ cabin │ VARCHAR │ YES │ │ │ │
│ embarked │ VARCHAR │ YES │ │ │ │
│ boat │ VARCHAR │ YES │ │ │ │
│ body │ BIGINT │ YES │ │ │ │
│ home.dest │ VARCHAR │ YES │ │ │ │
├─────────────┴─────────────┴─────────┴─────────┴─────────┴─────────┤
│ 14 rows 6 columns │
└───────────────────────────────────────────────────────────────────┘
D select pclass, sex, survived, count(*) from titanic group by pclass, sex, survived order by pclass, sex, survived;
┌────────┬─────────┬──────────┬──────────────┐
│ pclass │ sex │ survived │ count_star() │
│ int64 │ varchar │ int64 │ int64 │
├────────┼─────────┼──────────┼──────────────┤
│ 1 │ female │ 0 │ 5 │
│ 1 │ female │ 1 │ 139 │
│ 1 │ male │ 0 │ 118 │
│ 1 │ male │ 1 │ 61 │
│ 2 │ female │ 0 │ 12 │
│ 2 │ female │ 1 │ 94 │
│ 2 │ male │ 0 │ 146 │
│ 2 │ male │ 1 │ 25 │
│ 3 │ female │ 0 │ 110 │
│ 3 │ female │ 1 │ 106 │
│ 3 │ male │ 0 │ 418 │
│ 3 │ male │ 1 │ 75 │
├────────┴─────────┴──────────┴──────────────┤
│ 12 rows 4 columns │
└────────────────────────────────────────────┘